# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import string
import pickle
from bs4 import BeautifulSoup
from nltk.tokenize import word_tokenize    #导入tokenize包
from nltk.corpus import stopwords    #导入常见词表
from nltk.stem.porter import PorterStemmer    #导入PorterStemmer包
from sklearn.feature_extraction.text import TfidfVectorizer   
import dask.dataframe as da
from sklearn.decomposition import PCA
from sklearn.feature_selection import chi2, SelectKBest



def return_data_frame(path_label, path_text, if_other=True):
    '''
    将附件2重新构造，构造成一个两两配对的表格。
    '''
    data_label = pd.read_csv(path_label)
    data_text = pd.read_csv(path_text)
    
    # 将所有涉及 label==1 的问题 id 添加到一个 list 中
    id_label = []
    
    set_duplicate = []

    for _, row in data_label.iloc[:988,:].iterrows():
        # label==1 的有 989 条，即 0:989.
        duplicates = row['duplicates'].strip('[]').split(sep=', ')
        # 将 duplicates 列变成正常的字符串列表
        for duplicate in duplicates:
            # 对每一个 duplicates 的字符串
            # 转换为 int
            duplicate = int(duplicate.strip("''"))
            # 将所有 label = 1 的 questionID 放在一个列表 id_label 中。
            if duplicate not in id_label:
                id_label.append(duplicate)
            if row['questionID'] not in id_label:
                id_label.append(row['questionID'])
            
            # 将所有 label==1 的 questionID 对（集合），放入一个 list 中。
            set_tmp = set([row['questionID'], duplicate])
            if set_tmp not in set_duplicate:
                set_duplicate.append(set_tmp)
                
    if if_other:
        # 仅包含那些出现在 label==1 下的 id。
        question_id_all = list(data_text.id.values)
    else:
        question_id_all = id_label
    
    # 构建一个临时变量
    set_data = []
    # 遍历附件 1 的所有 id， 跟那些 label == 1 的 id 两两比较，从而组成一个数据集
    for i in question_id_all:
        # 遍历附件 1 的所有 id
        for j in id_label:
            # 遍历那些 label==1 的 id
                if i == j :
                    # 自己跟自己当然不比较
                    continue
                if set([i, j]) in set_duplicate:
                    # 若两者相似，则设为 1.
                    set_duplicate.remove(set([i, j]))
                    set_tmp = (i, j, 1)
                else:
                    # 不相似，设为 0.
                    set_tmp = (i, j, 0)
                set_data.append(set_tmp)
    # 构建成一个 data_frame
    df_data_label = pd.DataFrame(data=set_data,
                                columns=['questionID_1', 'questionID_2', 'label'])
    return df_data_label, id_label, set_duplicate

def string_process(path):
    '''
    将非结构的文本数据转换成 TF-IDF 的结构化的文本数据
    '''
    data_text = pd.read_csv(path)
    data_text_list = []
    # 将文本从 HTML 标签中提取出来。
    for text in data_text.text:
        soup = BeautifulSoup(text, "html5lib")    #删除所有 HTML5 的所有标签
        # 提取文本
        text = soup.get_text()
        # 构成一个 list
        data_text_list.append(text)
    
    # 删除多余空格
    data_strip = [string.strip() for string in data_text_list]
    # 将字母转换为小写
    data_lower = [string.lower() for string in data_strip]
    # 将符号全部转换成 空格
    # 临时变量
    data_replace_pun = []
    for text in data_lower:
        # 转换为空格
        tmp = "".join([" " if ch in string.punctuation else ch for ch in text]).split()
        tmp = " ".join(tmp)
        data_replace_pun.append(tmp)

    data_replace_pun[:5]
    
    stopwds = stopwords.words('english')   #导入英文常见词表到stopwds变量中
    data = []    #用以构成经过自然语言处理II后的语料库
    stemmer = PorterStemmer()

    for s in data_replace_pun:     #对语料库中的每一条文本进行拆分处理
        s_tokens = word_tokenize(s)     #对s进行拆分
        # 停用词过滤
        tokens_remove = [word for word in s_tokens if word not in stopwds]
        # 保留词根
        tokens_stem = [stemmer.stem(word) for word in tokens_remove]
        #构成新的字符串，每个单词以空格隔开
        data.append(" ".join(tokens_stem))  
        
    vectorizer = TfidfVectorizer()    #导入Tfidf方法模型
    tf_idf = vectorizer.fit_transform(data)    #使用Tfidf方法
    data = tf_idf.toarray()    #以列表形式输出语料库
    feature_name = vectorizer.get_feature_names()    #输出每一列的特征名称
    data_df = pd.DataFrame(data, columns=feature_name)    #以dataframe格式输出
    
    data_df['id'] = data_text.id
    return data_df

def sampling(df_data_label):
    '''
    分层采样
    '''
    # 提取出 labe==1 和 label==0 的数据，并对 label 为 0 的数据进行分层采样。
    # 采样比例为 0.005，不重复
    df_data_1 = df_data_label.loc[df_data_label['label']==1]
    df_data_0 = df_data_label.loc[df_data_label['label']==0]
    df_data_0_sample = df_data_0.groupby("label").sample(frac=0.005, replace=False)
    # 采样后的数据
    df_data_label = pd.concat([df_data_1, df_data_0_sample],
                                ignore_index=True)
    
    return df_data_label
    
def feature_selection(df_data_label, df_text, n=1004, alpha=0.2):
    '''
    筛选特征，去除无用特征。
    由于数据太大，不能够在整个数据集上筛选特征，
    因此，从 label==0 中筛选 n 个， label == 1不变。
    '''
    # 筛选 n 个 label==0 的数据
    df_data_1 = df_data_label.loc[df_data_label['label']==1]
    df_data_0 = df_data_label.loc[df_data_label['label']==0]
    df_data_0_sample = df_data_0.sample(n=n, replace=False)
    df_for_feature_selection = pd.concat([df_data_1, df_data_0_sample],
                                            ignore_index=True)
    # 提取出 label 和 quetionID
    y = df_for_feature_selection.label
    q1 = df_for_feature_selection.questionID_1
    q2 = df_for_feature_selection.questionID_2
    
    # 拼接文本
    q1 = df_text.merge(
                q1.to_frame(), 
                how='inner', 
                left_on='id', 
                right_on='questionID_1'
                )

    q1.drop(['questionID_1', 'id'], axis=1, inplace=True)

    q2 = df_text.merge(
                q2.to_frame(), 
                how='inner', 
                left_on='id', 
                right_on='questionID_2'
                )
                
    q2.drop(['questionID_2', 'id'], axis=1, inplace=True)
    
    # 用绝对值之差表示数据
    df_minus = (q1-q2).abs()
    _, p_values = chi2(df_minus, y)
    num = np.sum(p_values<alpha)
    
    filter_model = SelectKBest(chi2, k=num).fit(df_minus, y)
    return filter_model
    
def update_features(df, filter_model):
    '''
    使用卡方检验模型过滤数据
    '''
    # 提取 id 列
    id_column = df.id.copy()

    df.drop('id', axis=1, inplace=True)
    
    columns_save = filter_model.get_support(True)
    columns = df.columns[columns_save]
    
    data = filter_model.transform(df)
    
    df_after = pd.DataFrame(data=data, columns=columns)
    df_after['id'] = id_column

    return df_after
    
def data_final(df_after, df_data_label):
    # 分离数据
    y = df_data_label.label
    q1 = df_data_label.questionID_1
    q2 = df_data_label.questionID_2
    # 将问题编号 1 与过滤后的数据进行合并
    q1 = df_after.merge(
            q1.to_frame(), 
            how='inner', 
            left_on='id', 
            right_on='questionID_1'
            )

    q1.drop(['questionID_1', 'id'], axis=1, inplace=True)
    
    # 将问题编号 2 与过滤后的数据进行合并
    q2 = df_after.merge(
            q2.to_frame(), 
            how='inner', 
            left_on='id', 
            right_on='questionID_2'
            )

    q2.drop(['questionID_2', 'id'], axis=1, inplace=True)
    
    df = (q1-q2).abs()
    return df, y 
    
if __name__ == '__main__':
    path_label = r'../附件/附件2.csv'
    path_text = r'../附件/附件1.csv'
    
    df_data_label, _, _ = return_data_frame(path_label,
                                     path_text,
                                     if_other=True)
                                     
    print('粗放地合并', df_data_label.shape)
    # 保存数据
    df_data_label.to_csv(r'../附件/mid_data.csv', index=False)
    df_data_label = pd.read_csv(r'../附件/mid_data.csv')
    
    # 分层抽样 label==0 的数据
    df_data_label = sampling(df_data_label)
    print('选择性地合并： ', df_data_label.shape)
    
    # 附件 1 处理
    # 时间有些长
    df_text = string_process(path_text) 
    #df_text = pickle.load(open(r'../附件/label_text.pkl', 'rb'))
    pickle.dump(df_text, open(r'../附件/label_text.pkl', 'wb'))
    
    # 返回特征过滤器模型
    filter_model = feature_selection(df_data_label,
                                     df_text, n=1004,
                                     alpha=0.5)
    model_path = r'../附件/filter_model.pkl'
    pickle.dump(filter_model, open(model_path, 'wb'))
    
    # 对原始数据进行特征过滤
    df_after = update_features(df_text, filter_model)
    print('过滤后的数据： ', df_after.shape)
    
    X,y = data_final(df_after, df_data_label)
    
    # 保存数据
    pickle.dump(X, open(r'../附件/data_final.pkl', 'wb'))
    pickle.dump(y, open(r'../附件/y.pkl', 'wb'))
    
    
